/**
 * Copyright (C) 2009+ Spark Zheng
 *
 * @file        test/test_tcp_server.c 
 * @date        2010-04-24
 * @brief	test_module
 *		testsuit for libdserver, run a tcp/udp server with libevent
 *		-TCP protocol
 *		-UDP protocol
 * @see         dserver
 * @history
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#include <dlib/dlib.h>

#include "sgtree.h"
#include "libdserver.h"

#define IPS_DELIM	'-'
#define IPS_MASK	'/'

#define MAX_FIELD	32

#define OUTPUT		"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\nConnection: close\r\n\r\n%s"
#define JSON_DATA_A	"var IPData = new Array(\"%s\", \"\", \"\", \"%s\");"
#define JSON_DATA_B	"var IPData = new Array(\"%s\", \"\", \"%s\", \"\");"

S_SGTREE_HEAD *sg_beijing_root = NULL;
S_SGTREE_HEAD *sg_tianjin_root = NULL;
S_SGTREE_HEAD *sg_shanghai_root = NULL;
S_SGTREE_HEAD *sg_guangzhou_root = NULL;
S_SGTREE_HEAD *sg_shenzhen_root = NULL;
S_SGTREE_HEAD *sg_nanjing_root = NULL;
S_SGTREE_HEAD *sg_hangzhou_root = NULL;

static inline in_addr_t _pton(char *ip_str);
static int _dlib_pton(const char *ipstr, in_addr_t *ipl, in_addr_t *iph);

static int load_ipfile(const char *ipfile);
static void unload_ipfile(void);

static int session_init(void *arg);
static prot_ret_t session_worker(void *arg);
static void session_exit(void *arg);

int main(int argc, char **argv)
{
	int ret = 0;
	uint16_t port = 0;
	char host[INET_ADDRSTRLEN] = {0};
	char ipfile[1024] = {0};

	if (argc < 2) {
		fprintf(stderr, "Usage: need a parameter: configure file.\n");
		return -1;
	}

	/** config file */
	dlib_confl_t *confl = NULL;
	confl = dlib_confl_init(argv[1]);
	if (confl == NULL) {
		fprintf(stderr, "Config-file init error.\n");
		return -2;
	}

	char *str = NULL;
	str = dlib_confl_get_string(confl, "ipserver", "host", CONFL_STR_COPY);
	if (str == NULL) {
		snprintf(host, sizeof(host), "127.0.0.1");
	} else {
		snprintf(host, sizeof(host), str);
		dfree(str);
	}

	long integer = 0;
	ret = dlib_confl_get_integer(confl, "ipserver", "port", &integer);
	if (ret == -E_CONFL_FIELD_NOTEXIST || ret == -E_CONFL_ITEM_NOTEXIST) {
		port = 80;
	} else if (ret == 0) {
		port = (uint16_t)integer;
	}

	str = dlib_confl_get_string(confl, "ipserver", "ipfile", CONFL_STR_COPY);
	if (str == NULL) {
		snprintf(ipfile, sizeof(ipfile), "/etc/main_city.conf");
	} else {
		snprintf(ipfile, sizeof(ipfile), str);
		dfree(str);
	}

	/** load ipfile */
	ret = load_ipfile(ipfile);
	if (ret < 0) {
		fprintf(stderr, "load ipfile error.\n");
		dlib_confl_exit(confl);
		return -3;
	}

	fprintf(stderr, "build up http server with ip:%s, port:%u.\n", host, port);

	/** server begin */
	dserver_handler_t *handler = NULL;

	if (dserver_log_init("./log") < 0) {
		fprintf(stderr, "tcpserver_log init error.\n");
		ret = -4;
		goto end;
	}

	handler = dserver_tcpserver_init(host, port, 30);
	if (handler == NULL) {
		fprintf(stderr, "tcpserver_init error: %s\n", strerror(errno));
		ret = -5;
		goto end;
	}

	ret = dserver_set_handlers(handler, session_init, session_worker, session_exit);
	if (ret < 0) {
		fprintf(stderr, "tcpserver_set_handler error[%d]: %s\n", ret, strerror(errno));
		ret = -3;
		goto end;
	}

	ret = dserver_tcpserver_run(handler);
	if (ret < 0) {
		fprintf(stderr, "tcpserver_run error[%d]: %s\n", ret, strerror(errno));
		ret = -4;
		goto end;
	}
 
end:
	dserver_log_exit();
	unload_ipfile();
	dlib_confl_exit(confl);

	return ret;
}

static int session_init(void *arg)
{
	UNUSED_PARAM(arg);
	return PROTOCOL_SUCCESS;
}

static prot_ret_t session_worker(void *arg)
{
	if (arg == NULL) {
		fprintf(stderr, "protocol_session_worker parameters error.\n");
		return PROTOCOL_ERROR;
	}

	dserver_conn_t *conn = (dserver_conn_t *)arg;
	//prot_node_t *prot = conn->c_prot_node;

	if (conn->c_rsize == 0) {
		fprintf(stderr, "protocol_session_worker received data error.\n");
		return PROTOCOL_ERROR;
	}

	//fprintf(stderr, "BEGIN: rsize .......... = %d\n", conn->c_rsize);
	//fprintf(stderr, "%s\n", conn->c_rbuf);
	//fprintf(stderr, "EOF\n");

	size_t i = 0, j = 0;
	size_t token_len;
	str_token_t token[MAX_FIELD];

	uint8_t custom_flag = 0;
	in_addr_t client_ip = 0;
	in_addr_t client_iph = 0;
	char client_ip_str[INET_ADDRSTRLEN] = {0};

	token_len = str_tokenize(conn->c_rbuf, '\n', token, MAX_FIELD);
	for (; i < token_len - 1; i++) {
		size_t s_token_len = 0;
		str_token_t s_token[3];

		if (token[i].value == NULL) {
			continue;
		}

		if (strncasecmp(token[i].value, "GET ", 4) == 0) { /* check parameters */
			s_token_len = str_tokenize(token[i].value, ' ', s_token, 3);	
			if (s_token_len < 3) {
				fprintf(stderr, "HTTP-Header format error.\n");
				continue;	
			}

			size_t f_token_len = 0;
			str_token_t f_token[3];

			if (s_token[1].value == NULL) {
				continue;
			}

			f_token_len = str_tokenize(s_token[1].value, '?', f_token, 3);
			if (f_token_len < 3) {
				continue;
			}
			str_trim(f_token[1].value);

			size_t k_token_len = 0;
			str_token_t k_token[3];

			if (f_token[1].value == NULL) {
				continue;
			}

			k_token_len = str_tokenize(f_token[1].value, '&', k_token, 3);
			for (; j < k_token_len - 1; j++) {
				str_trim(k_token[j].value);
				if (k_token[j].value == NULL) {
					continue;
				}
				if (strncasecmp(k_token[j].value, "client_ip=", 10) == 0) {
					snprintf(client_ip_str, sizeof(client_ip_str), "%s", k_token[j].value + 10);
					custom_flag = 1;
					break;
				}
			}
		} else {
			if (custom_flag == 1) break;

			s_token_len = str_tokenize(token[i].value, ':', s_token, 3);
			if (s_token_len < 3) {
				continue;	
			}
			str_trim(s_token[0].value);

			if (s_token[0].value == NULL) {
				continue;
			}

			if (strcasecmp(s_token[0].value, "X-Forwarded-For") == 0)  {
				str_trim(s_token[1].value);
				snprintf(client_ip_str, sizeof(client_ip_str), "%s", s_token[1].value);
				break;
			}
		}
	}
	if (client_ip_str[0] == 0) {
		ipv4_to_str(((struct sockaddr_in *)(&conn->c_csa))->sin_addr.s_addr, client_ip_str);
	}
	//client_ip = str_to_ipv4(client_ip_str);
	(void)_dlib_pton(client_ip_str, &client_ip, &client_iph);

	//fprintf(stderr, "Client ip: %s[%d]\n", client_ip_str, client_ip);

	S_SGTREE_NODE *node = NULL;
	size_t json_data_len = 0;
	char json_data[64];

	node = sg_find(sg_shenzhen_root, client_ip, client_ip);
	if (node != NULL) {
		json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_A, client_ip_str, "深圳市");
		conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);
		goto output;
	}

	node = sg_find(sg_beijing_root, client_ip, client_ip);
	if (node != NULL) {
		json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_B, client_ip_str, "北京市");
		conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);
		goto output;
	}

	node = sg_find(sg_shanghai_root, client_ip, client_ip);
	if (node != NULL) {
		json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_B, client_ip_str, "上海市");
		conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);
		goto output;
	}

	node = sg_find(sg_guangzhou_root, client_ip, client_ip);
	if (node != NULL) {
		json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_A, client_ip_str, "广州市");
		conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);
		goto output;
	}

	node = sg_find(sg_tianjin_root, client_ip, client_ip);
	if (node != NULL) {
		json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_B, client_ip_str, "天津市");
		conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);
		goto output;
	}

	node = sg_find(sg_nanjing_root, client_ip, client_ip);
	if (node != NULL) {
		json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_A, client_ip_str, "南京市");
		conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);
		goto output;
	}

	node = sg_find(sg_hangzhou_root, client_ip, client_ip);
	if (node != NULL) {
		json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_A, client_ip_str, "杭州市");
		conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);
		goto output;
	}

	json_data_len = snprintf(json_data, sizeof(json_data), JSON_DATA_A, client_ip_str, "未知");
	conn->c_wsize = snprintf(conn->c_wbuf, DSERVER_TX_BUFLEN, OUTPUT, json_data_len, json_data);

output:
	/* sendback data to socket directively */
	if (dserver_tcp_data_sendout(conn) < 0) {
		fprintf(stderr, "protocol_session_worker sending data error.\n");
		return PROTOCOL_ERROR;
	}

//	return PROTOCOL_CONTINUE; /* server passively close */
//	return PROTOCOL_RESET; /* server cause RST */
//	return PROTOCOL_IGNORE; /* not used */ 
	return PROTOCOL_CLOSE; /* server actively close */
}

static void session_exit(void *arg)
{
	UNUSED_PARAM(arg);
	return;
}

static inline in_addr_t _pton(char *ip_str)
{
	in_addr_t ip = 0;
	char *dot = NULL;
	char *tmp = NULL;

	if ((dot = strchr(ip_str, '.')) != NULL) {
		*dot = '\0';
		ip += 256U * 256 * 256 * atoi(ip_str);
		tmp = dot + 1;
	} else {
		ip += 256U * 256 * 256 * atoi(ip_str);
		goto end;
	}

	if ((dot = strchr(tmp, '.')) != NULL) {
		*dot = '\0';
		ip += 256U * 256 * atoi(tmp);
		tmp = dot + 1;
	} else {
		ip += 256U * 256 * atoi(tmp);
		goto end;
	}

	if ((dot = strchr(tmp, '.')) != NULL) {
		*dot = '\0';
		ip += 256U * atoi(tmp);
		tmp = dot + 1;
	} else {
		ip += 256U * atoi(tmp);
		goto end;
	}

	ip += atoi(tmp);

end:
	return ip;
}

static int _dlib_pton(const char *ipstr, in_addr_t *ipl, in_addr_t *iph)
{
	uint8_t mask = 0;
	in_addr_t ip = 0;
	char ip_str[32] = {0};
	char *delim = NULL;
	char *tmp = NULL;

	snprintf(ip_str, 32, "%s", ipstr);

	if ((delim = strchr(ip_str, IPS_DELIM)) != NULL) { /* ip segment */
		*delim = '\0';
		tmp = delim + 1;

		ip = _pton(ip_str);
		if (ip == 0) return -1;

		*ipl = ip;

		ip = _pton(tmp);
		if (ip == 0) return -1;

		*iph = ip;
	} else if ((delim = strchr(ip_str, IPS_MASK)) != NULL) { /* ip mask */
		*delim = '\0';
		tmp = delim + 1;
		mask = (uint8_t)atoi(tmp);
		if (mask >= 32 || mask == 0) return -1;
		ip = _pton(ip_str);
		if (ip == 0) return -1;

		*ipl = (ip & (~0U << (32 - mask)));
		*iph = (ip | (~(~0U << (32 - mask))));
	} else { /* single ip */
		ip = _pton(ip_str);
		if (ip == 0) return -1;

		*ipl = ip;
		*iph = ip;
	}

	return 0;
}

static int load_ipfile(const char *ipfile)
{
	int ret = 0;
	FILE *fp = NULL;

	str_token_t token[MAX_FIELD];
	size_t token_len;

	char buffer[BUF_LEN + 1];
	char ips[BUF_LEN + 1];
	char ips_res[BUF_LEN + 1];
	in_addr_t ip_low;
	in_addr_t ip_high;

	if (ipfile == NULL) {
		return -1;
	}

	sg_beijing_root = sg_init();
	sg_tianjin_root = sg_init();
	sg_shanghai_root = sg_init();
	sg_guangzhou_root = sg_init();
	sg_shenzhen_root = sg_init();
	sg_nanjing_root = sg_init();
	sg_hangzhou_root = sg_init();

	if ((fp = fopen(ipfile, "r")) == NULL) {
		return -3;
	}
	
	memset(buffer, 0, sizeof(buffer));
	memset(ips, 0, sizeof(ips));
	memset(ips_res, 0, sizeof(ips_res));
	while (fgets(buffer, BUF_LEN, fp) != NULL) {

		token_len = str_tokenize(buffer, '\t', token, MAX_FIELD);
		snprintf(ips, sizeof(ips), "%s-%s", token[0].value, token[1].value);
		snprintf(ips_res, sizeof(ips_res), "%s-%s", token[1].value, token[0].value);
		str_trim(token[2].value);

		ret = _dlib_pton(ips, &ip_low, &ip_high);
		if (ret < 0) {
			memset(buffer, 0, sizeof(buffer));
			memset(ips, 0, sizeof(ips));
			memset(ips_res, 0, sizeof(ips_res));
			continue;
		}

		if (strcmp(token[2].value, "深圳市") == 0) {
			ret = sg_insert(sg_shenzhen_root, ip_low, ip_high);
			if (ret < 0) {
				fprintf(stderr, "sg_insert[%d] sz key error:[%s:%u:%u].\n", ret, ips, ip_low, ip_high);
				(void)_dlib_pton(ips_res, &ip_high, &ip_low);
				(void)sg_insert(sg_shenzhen_root, ip_low, ip_high);
			} else if (ret > 0) {
				fprintf(stderr, "sg_insert[%d] sz key exist:[%u:%u].\n", ret, ip_low, ip_high);
			}
		} else if (strcmp(token[2].value, "北京市") == 0) {
			ret = sg_insert(sg_beijing_root, ip_low, ip_high);
			if (ret < 0) {
				fprintf(stderr, "sg_insert[%d] bj key error:[%s:%u:%u].\n", ret, ips, ip_low, ip_high);
				(void)_dlib_pton(ips_res, &ip_high, &ip_low);
				(void)sg_insert(sg_shenzhen_root, ip_low, ip_high);
			} else if (ret > 0) {
				fprintf(stderr, "sg_insert[%d] bj key exist:[%u:%u].\n", ret, ip_low, ip_high);
			}
		} else if (strcmp(token[2].value, "上海市") == 0) {
			ret = sg_insert(sg_shanghai_root, ip_low, ip_high);
			if (ret < 0) {
				fprintf(stderr, "sg_insert[%d] sh key error:[%s:%u:%u].\n", ret, ips, ip_low, ip_high);
				(void)_dlib_pton(ips_res, &ip_high, &ip_low);
				(void)sg_insert(sg_shenzhen_root, ip_low, ip_high);
			} else if (ret > 0) {
				fprintf(stderr, "sg_insert[%d] sh key exist:[%u:%u].\n", ret, ip_low, ip_high);
			}
		} else if (strcmp(token[2].value, "广州市") == 0) {
			ret = sg_insert(sg_guangzhou_root, ip_low, ip_high);
			if (ret < 0) {
				fprintf(stderr, "sg_insert[%d] gz key error:[%s:%u:%u].\n", ret, ips, ip_low, ip_high);
				(void)_dlib_pton(ips_res, &ip_high, &ip_low);
				(void)sg_insert(sg_shenzhen_root, ip_low, ip_high);
			} else if (ret > 0) {
				fprintf(stderr, "sg_insert[%d] gz key exist:[%u:%u].\n", ret, ip_low, ip_high);
			}
		} else if (strcmp(token[2].value, "天津市") == 0) {
			ret = sg_insert(sg_tianjin_root, ip_low, ip_high);
			if (ret < 0) {
				fprintf(stderr, "sg_insert[%d] tj key error:[%s:%u:%u].\n", ret, ips, ip_low, ip_high);
				(void)_dlib_pton(ips_res, &ip_high, &ip_low);
				(void)sg_insert(sg_shenzhen_root, ip_low, ip_high);
			} else if (ret > 0) {
				fprintf(stderr, "sg_insert[%d] tj key exist:[%u:%u].\n", ret, ip_low, ip_high);
			}
		} else if (strcmp(token[2].value, "南京市") == 0) {
			ret = sg_insert(sg_nanjing_root, ip_low, ip_high);
			if (ret < 0) {
				fprintf(stderr, "sg_insert[%d] nj key error:[%s:%u:%u].\n", ret, ips, ip_low, ip_high);
				(void)_dlib_pton(ips_res, &ip_high, &ip_low);
				(void)sg_insert(sg_shenzhen_root, ip_low, ip_high);
			} else if (ret > 0) {
				fprintf(stderr, "sg_insert[%d] nj key exist:[%u:%u].\n", ret, ip_low, ip_high);
			}
		} else if (strcmp(token[2].value, "杭州市") == 0) {
			ret = sg_insert(sg_hangzhou_root, ip_low, ip_high);
			if (ret < 0) {
				fprintf(stderr, "sg_insert[%d] hz key error:[%s:%u:%u].\n", ret, ips, ip_low, ip_high);
				(void)_dlib_pton(ips_res, &ip_high, &ip_low);
				(void)sg_insert(sg_shenzhen_root, ip_low, ip_high);
			} else if (ret > 0) {
				fprintf(stderr, "sg_insert[%d] hz key exist:[%u:%u].\n", ret, ip_low, ip_high);
			}
		} else {
			fprintf(stderr, "unexpect input city: %s.\n", token[2].value);
		}

		memset(buffer, 0, sizeof(buffer));
		memset(ips, 0, sizeof(ips));
	}
	
	fclose(fp);

	return 0;
}

static void unload_ipfile()
{
	return;
}
